import { extendClassProps, ObjResettingWithEvent, react, reactArray, reactArrayWithUndefined, ReactivePropsToNativePropsAndChanged, track } from "xbsj-base";
import { PointEditing } from "..";
import { CzmModelPrimitive, ESCesiumViewer } from "@czmSrc/index";
import { computeCzmModelMatrix, positionToCartesian } from "@czmSrc/utils";
import * as Cesium from 'cesium';
import { ESJNativeNumber16 } from "earthsdk3";
import { BasePlane, HeadingPlane, PitchPlane, RollPlane } from "./GeoRotatorPlane";
import { GeoRotatorCircle } from "./GeoRotatorCircle";
import { RotatorRunning } from "./RotatorRunning";

export type RotatorPlaneType = 'none' | 'heading' | 'pitch' | 'roll';

export class RotationEditing extends PointEditing {
    private _hoveredPlaneType = this.disposeVar(react<RotatorPlaneType>('none'));
    get hoveredPlaneType() { return this._hoveredPlaneType.value; }
    set hoveredPlaneType(value: RotatorPlaneType) { this._hoveredPlaneType.value = value; }
    get hoveredPlaneTypeChanged() { return this._hoveredPlaneType.changed; }

    private _movingPlaneType = this.disposeVar(react<RotatorPlaneType>('none'));
    get movingPlaneType() { return this._movingPlaneType.value; }
    set movingPlaneType(value: RotatorPlaneType) { this._movingPlaneType.value = value; }
    get movingPlaneTypeChanged() { return this._movingPlaneType.changed; }

    static rotationNum = { heading: 0, pitch: 1, roll: 2 };
    static rotationFuncs = {
        heading: ([heading, pitch, roll]: [number, number, number]): [number, number, number] => [0, 0, 0],
        pitch: ([heading, pitch, roll]: [number, number, number]): [number, number, number] => [heading, 0, -90],
        roll: ([heading, pitch, roll]: [number, number, number]): [number, number, number] => [heading, pitch + 90, 0],
    };
    // 圆地图效果
    private _circles;
    get circles() { return this._circles; }
    // 照相机地图效果
    private _czmCameraModel?: CzmModelPrimitive;
    get czmCameraModel() { return this._czmCameraModel; }

    private _cartesian = undefined as Cesium.Cartesian3 | undefined;
    get cartesian() { return this._cartesian; }

    get czmViewer() { return this._czmViewer; }

    // 用于判断是否相交的平面
    private _planes = {
        heading: this.disposeVar(new HeadingPlane(this)),
        pitch: this.disposeVar(new PitchPlane(this)),
        roll: this.disposeVar(new RollPlane(this)),
    }
    getPlane(type: Exclude<RotatorPlaneType, 'none'>): BasePlane {
        return this._planes[type];
    }
    private _rotatorRunningResetting = this.disposeVar(new ObjResettingWithEvent(this.enabledChanged, () => {
        if (!this.enabled) return undefined;
        return new RotatorRunning(this);
    }));
    get rotatorRunningResetting() { return this._rotatorRunningResetting; }
    constructor(private _czmViewer: ESCesiumViewer) {
        super();
        const czmViewer = this._czmViewer;
        this._circles = {
            heading: this._createRotatorCircle(czmViewer, 'heading'),
            pitch: this._createRotatorCircle(czmViewer, 'pitch'),
            roll: this._createRotatorCircle(czmViewer, 'roll'),
        };
        const viewer = czmViewer.viewer;
        if (!viewer) return;
        {
            const update = () => {
                this._cartesian = this.position && positionToCartesian(this.position) || undefined;
            };
            update();
            this.dispose(this.positionChanged.disposableOn(update));
        }
        this._createCameraModel(czmViewer);
    }
    private _createCameraModel(czmViewer: ESCesiumViewer) {
        this._czmCameraModel = new CzmModelPrimitive(czmViewer);
        this._czmCameraModel.url = '${earthsdk3-assets-script-dir}/assets/glb/camera1/camera1.gltf';
        this._czmCameraModel.localRotation = [90, 0, 0];
        this._czmCameraModel.allowPicking = false;
        this._czmCameraModel.opaquePass = 'OVERLAY';
        this.dispose(track([this._czmCameraModel, 'pixelSize'], [this, 'pixelSize'], (v: number) => v * .1));
        this.dispose(track([this._czmCameraModel, 'position'], [this, 'position']));
        this.dispose(track([this._czmCameraModel, 'rotation'], [this, 'rotation']));
        {
            const update = () => {
                const rm = computeCzmModelMatrix({
                    rotation: this.selfRotation,
                });
                if (!rm) throw new Error(`!rm`);
                if (this._czmCameraModel)
                    this._czmCameraModel.localModelMatrix = Cesium.Matrix4.toArray(rm) as ESJNativeNumber16;
            }
            update();
            this.dispose(this.selfRotationChanged.disposableOn(update));
        }
        {
            const update = () => {
                this.circles.heading.show = this.enabled;
                this.circles.pitch.show = this.enabled;
                this.circles.roll.show = this.enabled;
                if (this._czmCameraModel)
                    this._czmCameraModel.show = this.enabled && this.showHelper;
            };
            update();
            this.dispose(this.enabledChanged.disposableOn(update));
            this.dispose(this.showHelperChanged.disposableOn(update));
        }
    }
    private _createRotatorCircle(czmViewer: ESCesiumViewer, type: Exclude<RotatorPlaneType, 'none'>) {
        const circle = this.ad(new GeoRotatorCircle(czmViewer));
        this.dispose(track([circle, 'position'], [this, 'position']));
        this.dispose(track([circle, 'pixelSize'], [this, 'pixelSize']));
        this.dispose(track([circle, 'debug'], [this, 'debug']));
        this.dispose(track([circle, 'rotation'], [this, 'rotation']));
        {
            const update = () => {
                circle.selfRotation = RotationEditing.rotationFuncs[type](this.selfRotation);
                circle.circleRotation = this.selfRotation[RotationEditing.rotationNum[type]];
            };
            update();
            this.dispose(this.selfRotationChanged.disposableOn(update));
        }
        {
            const update = () => {
                const moving = this.movingPlaneType === type;
                const hovered = this.hoveredPlaneType === type;
                const alpha = (moving || hovered) ? 1.0 : 0.8;
                const color = [0, 0, 0, alpha] as [number, number, number, number];
                color[RotationEditing.rotationNum[type]] = 1;
                circle.color = color;
            };
            update();
            this.dispose(this.movingPlaneTypeChanged.disposableOn(update));
            this.dispose(this.hoveredPlaneTypeChanged.disposableOn(update));
        }
        return circle;
    }
}
export namespace RotationEditing {
    export const createDefaultProps = () => ({
        ...PointEditing.createDefaultProps(),
        rotation: reactArray<[number, number, number]>([0, 0, 0]),
        selfRotation: reactArray<[number, number, number]>([0, 0, 0]),
        pixelSize: 300,
        showHelper: false,
        debug: false,
    });
}
extendClassProps(RotationEditing.prototype, RotationEditing.createDefaultProps);
export interface RotationEditing extends ReactivePropsToNativePropsAndChanged<ReturnType<typeof RotationEditing.createDefaultProps>> { }